﻿using System;
using System.Text;
using Microsoft.Win32;

namespace ApiScanner.Core
{
    internal class ComClassInfo
    {
        /// <summary>
        /// Gets class information from remote machine and dumps it to a string
        /// </summary>
        /// <param name="className">Prog ID of class to dump, e.g. "MyEventSink.Foo"</param>
        /// <param name="machineName">DNS hostname of server</param>
        /// <returns>String representing class ID registry subtree</returns>
        public static string GetClassInfo(string className, string machineName)
        {
            var myComObject = new StringBuilder(null);
            const string defaultString = null;
            string classId;
            RegistryKey classesRoot;

            myComObject.Append("[" + className);

            try
            {
                classesRoot = RegistryKey.OpenRemoteBaseKey(RegistryHive.ClassesRoot, machineName);
            }
            catch (Exception)
            {
                // can't bind to remote registry
                myComObject.Append("]");
                return myComObject.ToString();
            }

            // ok, we're in - try to grab this COM object by it's Prog ID and get the CLSID 
            // so we can see who's registered it
            using (var classToDump = classesRoot.OpenSubKey(className + "\\CLSID"))
            {
                if (classToDump != null)
                {
                    classId = classToDump.GetValue(defaultString).ToString();
                    myComObject.Append("/ClassId=" + classId);
                }
                else
                {
                    // Can't find the Prog ID
                    myComObject.Append("]");
                    myComObject.Replace(",", ";");
                    return myComObject.ToString();
                }
            }


            // now find the binary that is invoked and related information, one level deep
            using (RegistryKey inProcServerKey = classesRoot.OpenSubKey("CLSID\\" + classId))
            {
                if (inProcServerKey != null)
                {
                    var valueNames = inProcServerKey.GetValueNames();
                    myComObject.Append("/Values:");
                    foreach (var valueName in valueNames)
                    {
                        myComObject.AppendFormat("{0}={1}", ((valueName == "") ? "(Default)" : (string.Format("&{0}", valueName))), inProcServerKey.GetValue(valueName));
                    }

                    var keyNames = inProcServerKey.GetSubKeyNames();

                    foreach (var keyName in keyNames)
                    {
                        using (var subKey = inProcServerKey.OpenSubKey(keyName))
                        {
                            if (subKey == null) continue;
                            myComObject.AppendFormat("/[SubKey:{0}/Values:", keyName);

                            valueNames = subKey.GetValueNames();

                            foreach (string valueName in valueNames)
                            {
                                myComObject.AppendFormat("{0}={1}", ((valueName == "") ? "(Default)" : (string.Format("&{0}", valueName))), subKey.GetValue(valueName));
                            }
                            myComObject.Append("]");
                        }
                    }
                }
            }
            myComObject.Append("]");
            myComObject.Replace(",", ";");
            return myComObject.ToString();
        }
    }
}
